﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using MemoryPack;

#if UNITY_EDITOR
using Sirenix.OdinInspector;
#endif

namespace Hsenl {
    [Flags]
    internal enum ComponentImplementFlag {
        ImplementOnDeserialized = 1 << 0,
        ImplemenOnDeserializedOverall = 1 << 1,
        ImplemenOnAwake = 1 << 2,
        ImplemenOnEnable = 1 << 3,
        ImplemenOnStart = 1 << 4,
        ImplemenOnUpdate = 1 << 5,
        ImplemenOnLateUpdate = 1 << 6,
        ImplemenOnDisable = 1 << 7,
        ImplemenOnDestroy = 1 << 8,
        ImplemenOnDisposed = 1 << 9,
        ImplemenOnBeforeParentChange = 1 << 10,
        ImplemenOnParentChanged = 1 << 11,
        ImplemenOnComponentAdd = 1 << 12,
        ImplemenOnComponentRemove = 1 << 13,
        ImplemenOnChildAdd = 1 << 14,
        ImplemenOnChildRemove = 1 << 15,
        ImplemenOnAppQuit = 1 << 16,
        ImplemenOnSceneChanged = 1 << 17,
    }

    [MemoryPackable(GenerateType.NoGenerate)]
    public abstract partial class Component : Object, IComponent {
        private static int _cacherCount;

        public static int CacherCount => _cacherCount;

        // 在框架里, 只要是和组件类有关的 (包括父类、继承的接口), 都会被指定一个编号, 并记录再此, 该编号是唯一的, 该编号是按01234...顺序分配的, 该编号为ComponentTypeIndex
        // ComponentTypeCacher.originalIndex == 该缓存器所代表的类型的编号
        // ComponentTypeCacher.bits 里保存了该类型所有的子类型的TypeIndex, 包括该类型自己的TypeIndex
        internal static readonly Dictionary<Type, ComponentTypeCacher> typeLookupTable = new(); // key: 某类型的哈希值, value: 某类型的'组件类型缓存器'
        internal static readonly MultiQueue<Type, (ComponentTypeCacher require, Type add)> requireComponentLookupTable = new();
        internal static readonly Dictionary<int, ComponentTypeCacher> requireInverseLookupTable = new();
        internal static readonly Dictionary<Type, ComponentOptionsAttribute> componentOptions = new();
        internal static readonly Dictionary<Type, ComponentImplementInfo> componentImplementInfos = new();

        // 缓冲类型映射表是许多操作的基础, 所以应该被最先执行
        [OnEventSystemInitialized, Order(-500)]
        private static void CacheTypeHashtable() {
            // 获取所有组件类的类型的哈希映射表
            // 逻辑如此, 假设现在有3个类
            // Class1、2、3
            // 我们先给三个类规定一个编号, 就按照遍历的顺序来定, 那这三个类的编号分别是0 1 2
            // 三个类的继承关系是
            // Class1 是基类
            // Class2 继承自 Class1
            // Class3 继承自 Class2
            // 那么, 他们三个的哈希映射表分别是
            // Class1: 0, 1, 2
            // Class2: 1, 2
            // Class3: 2,
            // 第一位是自己的编号, 后面的是他所有子类的编号, 但因为位列表是不存在严格意义上的"第一位"的, 所以我们需要一个额外的originalIndex来记录每个类自己的原始编号

            // 第一步, 提前做一些缓存, 留作后用
            typeLookupTable.Clear();
            var map = new MultiList<Type, Type>(); // key: selfType, value: baseTypes
            var uniques = new HashSet<Type>(); // 所有会被分配编号的类、接口
            var componentType = typeof(Component);
            // 组件类也记录, 但不需要记录父类
            map.Add(componentType, null);
            uniques.Add(componentType);
            foreach (var type in EventSystem.GetAllTypes()) {
                if (type.IsSubclassOf(componentType)) {
                    uniques.Add(type);
                    // 如果该类是一个继承自组件的类, 则遍历其所有的父类
                    foreach (var baseType in AssemblyHelper.GetBaseTypes(type)) {
                        // 如果是一个接口类, 则直接添加
                        if (baseType.IsInterface) {
                            map.Add(type, baseType);
                            uniques.Add(baseType);
                        }
                        // 如果是class, 则要判断是不是组件类的子类, 因为我们最多缓存到组件类, 不再继续往上缓存
                        else {
                            if (baseType.IsSubclassOf(componentType)) {
                                map.Add(type, baseType);
                                uniques.Add(baseType);
                            }
                        }
                    }

                    // 最后补充添加组件类
                    map.Add(type, componentType);
                }
            }

            var num = 0;
            _cacherCount = uniques.Count;
            // 第二步, 给每个类分配编号
            foreach (var type in uniques) {
                var cacher = ComponentTypeCacher.Create(num++);
                cacher.Add(cacher.originalIndex);
                typeLookupTable[type] = cacher;
            }

            // 第三步, 给每个类添加基类到映射表
            foreach (var kv in map) {
                var cacher = typeLookupTable[kv.Key];
                foreach (var baseType in kv.Value) {
                    if (baseType == null) continue;
                    typeLookupTable[baseType].Add(cacher.originalIndex);
                }
            }
        }

        [OnEventSystemInitialized, Order(-400)]
        private static void CacheRequireTypes() {
            requireComponentLookupTable.Clear();
            foreach (var type in EventSystem.GetTypesOfAttribute(typeof(RequireComponentAttribute))) {
                var attr = type.GetCustomAttribute<RequireComponentAttribute>(true);
                if (requireComponentLookupTable.TryGetValue(type, out var list1)) {
                    foreach (var tuple in list1) {
                        if (tuple.add == attr.addType) {
                            // 一个组件A重复Require了组件B
                            throw new Exception($"require component repetition, '{type.Name}' - '{attr.addType.Name}'");
                        }
                    }
                }

                if (requireComponentLookupTable.TryGetValue(attr.addType, out var list2)) {
                    foreach (var tuple in list2) {
                        if (tuple.add == type) {
                            // 一个组件A Require了组件B, 同时, 组件B也Require了组件A
                            throw new Exception($"require component conflict, '{type.Name}' - '{attr.addType.Name}'");
                        }
                    }
                }

                var typeCacher = typeLookupTable[attr.requireType];
                requireComponentLookupTable.Enqueue(type, (typeCacher, attr.addType));
                var componentIndex = GetComponentIndex(type);
                if (!requireInverseLookupTable.TryGetValue(componentIndex, out var value)) {
                    value = ComponentTypeCacher.CreateEmpty();
                    requireInverseLookupTable[componentIndex] = value;
                }

                value.Add(GetComponentIndex(type));
            }
        }

        [OnEventSystemInitialized, Order(-400)]
        private static void CacheComponentOptions() {
            componentOptions.Clear();
            foreach (var type in EventSystem.GetTypesOfAttribute(typeof(ComponentOptionsAttribute))) {
                var attr = type.GetCustomAttribute<ComponentOptionsAttribute>(true);
                componentOptions[type] = attr;
            }
        }

        [OnEventSystemInitialized, Order(-400)]
        private static void CacheComponentImplementInfo() {
            componentImplementInfos.Clear();
            var types = new List<Type>();
            var componentType = typeof(Component);
            foreach (var type in EventSystem.GetAllTypes()) {
                if (type.IsSubclassOf(componentType)) {
                    types.Add(type);
                }
            }

            var param1 = new[] { typeof(Entity) };
            var param2 = new[] { typeof(Component) };
            var param3 = new[] { typeof(Entity), typeof(int) };
            foreach (var type in types) {
                var info = new ComponentImplementInfo();
                componentImplementInfos[type] = info;
                // @formatter:off
                if (IsOverridden(type, "OnDeserializedInternal") || IsOverridden(type, "OnDeserialized")) info.implementOnDeserialized = true;
                if (IsOverridden(type, "OnDeserializedOverallInternal") || IsOverridden(type, "OnDeserializedOverall")) info.implemenOnDeserializedOverall = true;
                if (IsOverridden(type, "OnAwakeInternal") || IsOverridden(type, "OnAwake")) info.implemenOnAwake = true;
                if (IsOverridden(type, "OnEnable")) info.implemenOnEnable = true;
                if (IsOverridden(type, "OnStartInternal") || IsOverridden(type, "OnStart")) info.implemenOnStart = true;
                if (IsOverridden(type, "OnUpdate")) info.implemenOnUpdate = true;
                if (IsOverridden(type, "OnLateUpdate")) info.implemenOnLateUpdate = true;
                if (IsOverridden(type, "OnDisable")) info.implemenOnDisable = true;
                if (IsOverridden(type, "OnDestroyInternal") || IsOverridden(type, "OnDestroy")) info.implemenOnDestroy = true;
                if (IsOverridden(type, "OnDisposedInternal") || IsOverridden(type, "OnDisposed")) info.implemenOnDisposed = true;
                if (IsOverridden(type, "OnBeforeParentChangeInternal", param3) || IsOverridden(type, "OnBeforeParentChange", param3)) info.implemenOnBeforeParentChange = true;
                if (IsOverridden(type, "OnParentChangedInternal", param3) || IsOverridden(type, "OnParentChanged", param3)) info.implemenOnParentChanged = true;
                if (IsOverridden(type, "OnComponentAddInternal", param2) || IsOverridden(type, "OnComponentAdd", param2)) info.implemenOnComponentAdd = true;
                if (IsOverridden(type, "OnComponentRemoveInternal", param2) || IsOverridden(type, "OnComponentRemove", param2)) info.implemenOnComponentRemove = true;
                if (IsOverridden(type, "OnChildAddInternal", param1) || IsOverridden(type, "OnChildAdd", param1)) info.implemenOnChildAdd = true;
                if (IsOverridden(type, "OnChildRemoveInternal", param1) || IsOverridden(type, "OnChildRemove", param1)) info.implemenOnChildRemove = true;
                if (IsOverridden(type, "OnAppQuit")) info.implemenOnAppQuit = true;
                if (IsOverridden(type, "OnSceneChanged")) info.implemenOnSceneChanged = true;
                // @formatter:on

                if (!info.implementOnDeserialized
                    && !info.implemenOnAwake
                    && !info.implemenOnDeserializedOverall
                    && !info.implemenOnEnable
                    && !info.implemenOnStart
                    && !info.implemenOnUpdate
                    && !info.implemenOnLateUpdate
                    && !info.implemenOnDisable
                    && !info.implemenOnDestroy
                    && !info.implemenOnDisposed
                    && !info.implemenOnBeforeParentChange
                    && !info.implemenOnParentChanged
                    && !info.implemenOnComponentAdd
                    && !info.implemenOnComponentRemove
                    && !info.implemenOnChildAdd
                    && !info.implemenOnChildRemove
                    && !info.implemenOnAppQuit
                    && !info.implemenOnSceneChanged
                   ) {
                    info.isNull = true;
                }
            }

            return;

            bool IsOverridden(Type derivedType, string methodName, Type[] parameters = null) {
                parameters ??= Type.EmptyTypes;
                var method = derivedType.GetMethod(
                    methodName,
                    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
                    null,
                    parameters,
                    null
                );
                if (method == null)
                    return false;

                var isoverride = method.GetBaseDefinition().DeclaringType != method.DeclaringType;
                return isoverride;
            }
        }

        [OnEventSystemUnload]
        private static void Unload() {
            typeLookupTable.Clear();
            requireComponentLookupTable.Clear();
            requireInverseLookupTable.Clear();
            componentOptions.Clear();
            componentImplementInfos.Clear();
        }

        public static ComponentTypeCacher CombineComponentType(IList<Type> types, int start = 0, int length = -1) {
            if (length == -1) length = types.Count;
            var result = ComponentTypeCacher.CreateEmpty();
            for (var i = start; i < length; i++) {
                result.Add(typeLookupTable[types[i]].originalIndex);
            }

            return result;
        }

        public static ComponentTypeCacher CombineComponentType(params Type[] types) {
            var result = ComponentTypeCacher.CreateEmpty();
            for (int i = 0, len = types.Length; i < len; i++) {
                result.Add(typeLookupTable[types[i]].originalIndex);
            }

            return result;
        }

        public static void CombineComponentType(ComponentTypeCacher cacher, Type type) {
            cacher.Add(typeLookupTable[type].originalIndex);
        }

        public static void CombineComponentType(ComponentTypeCacher cacher, params Type[] types) {
            for (int i = 0, len = types.Length; i < len; i++) {
                cacher.Add(typeLookupTable[types[i]].originalIndex);
            }
        }

        public static int GetComponentIndex<T>() where T : class {
            return GetComponentIndex(typeof(T));
        }

        public static int GetComponentIndex(Type componentType) {
            if (typeLookupTable.TryGetValue(componentType, out var cacher)) {
                return cacher.originalIndex;
            }

            return -1;
        }

        [MemoryPackInclude, MemoryPackOrder(1)]
        internal ComponentImplementFlag implementFlag;

        // 根据 flag 来判断该组件有没有实现某些事件, 可以在一些地方节省不必要的性能开支, 这就是该功能的核心用处.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal bool IsFlag(ComponentImplementFlag flag) {
            return (this.implementFlag & flag) == flag;
        }

        internal void AddFlag(ComponentImplementFlag flag) {
            this.implementFlag |= flag;
        }

        internal void RemoveFlag(ComponentImplementFlag flag) {
            this.implementFlag &= ~flag;
        }

#if UNITY_EDITOR
        [ShowInInspector]
        [MemoryPackIgnore]
        public string ViewName => this.IsDisposed ? "Null" : this.Name;
#endif

        [NonSerialized] // unity那个 depth limit 10警告
        [MemoryPackIgnore]
        internal Entity entity;

        [MemoryPackIgnore]
        internal bool awaked;

        [MemoryPackOrder(2)]
        [MemoryPackInclude]
        internal bool enable = true;

        [MemoryPackIgnore]
        internal bool registedUpdate;

        [MemoryPackIgnore]
        internal bool registedLateUpdate;

        [MemoryPackIgnore]
        private int _componentIndex = -1;

        [MemoryPackIgnore]
        public int ComponentIndex {
            get {
                // component index 是由框架在这次进程, 给每个组件分配的唯一编号
                if (this._componentIndex == -1) {
                    this._componentIndex = GetComponentIndex(this.GetType());
                    if (this._componentIndex == -1)
                        throw new Exception($"get component index fail '{this.GetType().Name}'");
                }

                return this._componentIndex;
            }
        }

        [MemoryPackIgnore]
        public Entity Entity {
            get {
                this.CheckDisposedException("Component is disposed, can't get entity");
                return this.entity;
            }
        }

        [MemoryPackIgnore]
        public Entity Parent => this.Entity.parent;

        [MemoryPackIgnore]
        public string Name => this.Entity.Name;

        [MemoryPackIgnore]
        public Scene Scene => this.Entity.scene;

#if UNITY_EDITOR
        [ShowInInspector]
#endif
        [MemoryPackIgnore]
        public bool Awaked => this.awaked;

#if UNITY_EDITOR
        [ShowInInspector]
#endif
        [MemoryPackIgnore]
        public bool Enable {
            get => this.enable;
            set {
                this.CheckDisposedException("Component is disposed, can't set enable");
                if (this.enable == value)
                    return;

                this.enable = value;

                if (this.Entity.RealActive) {
                    if (this.enable) {
                        this.InternalOnEnable();
                    }
                    else {
                        this.InternalOnDisable(true);
                    }
                }

#if UNITY_EDITOR
                this.PartialOnEnableSelfChanged(value);
#endif
            }
        }

#if UNITY_EDITOR
        [ShowInInspector]
#endif
        [MemoryPackIgnore]
        public bool RealEnable {
            get {
                this.CheckDisposedException("Component is disposed");
                if (this.enable == false) return false;
                return this.Entity.RealActive;
            }
        }

        [MemoryPackIgnore]
        public int ChildCount => this.Entity.ChildCount;

        [MemoryPackIgnore]
        public Bitlist Tags => this.Entity.Tags;

        [MemoryPackIgnore]
        public Transform transform => this.Entity.transform;

        public void Reenable() {
            this.CheckDisposedException("Component is disposed, can't set enable");
            this.Enable = false;
            this.Enable = true;
        }

        internal sealed override void Dispose() {
            base.Dispose();
            this.entity = null;
            this.awaked = false;
            this.enable = true;
            this._componentIndex = -1;

            try {
                this.OnDisposedInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnDisposed();
            }
            catch (Exception e) {
                Log.Error(e);
            }
#if UNITY_EDITOR
            this.PartialOnDestroyFinish();
#endif
        }

        internal void InternalOnDeserialized() {
            try {
                this.OnDeserializedInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnDeserialized();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnDeserializedOverall() {
            try {
                this.OnDeserializedOverallInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnDeserializedOverall();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnAwake() {
            if (this.awaked)
                return;
            this.awaked = true;

            EventSystemManager.Instance.RegisterStart(this);

            try {
                this.OnAwakeInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnAwake();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnEnable() {
            if (!this.enable) return;
            this.InternalOnAwake();

            if (this.IsFlag(ComponentImplementFlag.ImplemenOnUpdate) && !this.registedUpdate) {
                EventSystemManager.Instance.RegisterUpdate(this);
            }

            if (this.IsFlag(ComponentImplementFlag.ImplemenOnLateUpdate) && !this.registedLateUpdate) {
                EventSystemManager.Instance.RegisterLateUpdate(this);
            }

            try {
                this.OnEnable();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            PublicEvents.OnComponentEnabled(this);
        }

        internal void InternalOnStart() {
            try {
                this.OnStartInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnStart();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnUpdate() {
            try {
                this.OnUpdate();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnLateUpdate() {
            try {
                this.OnLateUpdate();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnDisable(bool forceExecute = false) {
            if (!forceExecute && !this.enable) return;

            try {
                this.OnDisable();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            PublicEvents.OnComponentDisabled(this);
        }

        internal void InternalOnDestroy() {
            try {
                this.OnDestroyInternal();
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnDestroy();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnBeforeParentChange(Entity futrueParent, int layer) {
            try {
                this.OnBeforeParentChangeInternal(futrueParent, layer);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnBeforeParentChange(futrueParent, layer);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnParentChanged(Entity previousParent, int layer) {
            try {
                this.OnParentChangedInternal(previousParent, layer);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnParentChanged(previousParent, layer);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnComponentAdd(Component component) {
            try {
                this.OnComponentAddInternal(component);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnComponentAdd(component);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnComponentRemove(Component component) {
            try {
                this.OnComponentRemoveInternal(component);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnComponentRemove(component);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnChildAdd(Entity child) {
            try {
                this.OnChildAddInternal(child);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnChildAdd(child);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnChildRemove(Entity child) {
            try {
                this.OnChildRemoveInternal(child);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            try {
                this.OnChildRemove(child);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnAppQuit() {
            try {
                this.OnAppQuit();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        internal void InternalOnSceneChanged() {
            try {
                this.OnSceneChanged();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        // 之所以每个函数都增加了一个Internal版本, 主要因为框架内部有时也需要用到这些函数, 但如此以来, 在框架外部实现该函数就必须要附带base.Function()的调用, 就会很乱

        internal virtual void OnDeserializedInternal() { }

        // 当自己序列化完成时. 此时, 整个实体的组件, 父子, 都已经装配完毕, 只是此时还未触发任何事件.
        protected virtual void OnDeserialized() { }

        internal virtual void OnDeserializedOverallInternal() { }

        // 跟上面唯一的区别就是, 所有该触发的事件都触发了.
        protected virtual void OnDeserializedOverall() { }

        internal virtual void OnAwakeInternal() { }

        protected virtual void OnAwake() { }

        protected virtual void OnEnable() { }

        internal virtual void OnStartInternal() { }

        protected virtual void OnStart() { }

        protected virtual void OnUpdate() { }

        protected virtual void OnLateUpdate() { }

        protected virtual void OnDisable() { }

        internal virtual void OnDestroyInternal() { }

        protected virtual void OnDestroy() { }

        internal virtual void OnDisposedInternal() { }

        protected virtual void OnDisposed() { }

        internal virtual void OnBeforeParentChangeInternal(Entity futrueParent, int layer) { }

        protected virtual void OnBeforeParentChange(Entity futrueParent, int layer) { }

        internal virtual void OnParentChangedInternal(Entity previousParent, int layer) { }

        protected virtual void OnParentChanged(Entity previousParent, int layer) { }

        internal virtual void OnComponentAddInternal(Component component) { }

        protected virtual void OnComponentAdd(Component component) { }

        internal virtual void OnComponentRemoveInternal(Component component) { }

        protected virtual void OnComponentRemove(Component component) { }

        internal virtual void OnChildAddInternal(Entity child) { }

        protected virtual void OnChildAdd(Entity child) { }

        internal virtual void OnChildRemoveInternal(Entity child) { }

        protected virtual void OnChildRemove(Entity child) { }

        protected virtual void OnAppQuit() { }

        protected virtual void OnSceneChanged() { }

        #region partial

#if UNITY_EDITOR
        partial void PartialOnEnableSelfChanged(bool enab);

        partial void PartialOnDestroyFinish();
#endif

        #endregion
    }
}